#include "WinWindow.h"
#include "ExtException.h"
#include "eerrors.h"

#define WINDOW_CLASS L"VEngine_Window_{285AD451-A632-4DDB-871B-175DD2AD5FE1}"

WinWindow::WinWindow(const WindowSetup& setup)
	: m_setup(setup)
    , m_window(NULL) {

    WNDCLASS sWC;
    sWC.style = CS_HREDRAW | CS_VREDRAW;
    sWC.lpfnWndProc = wndProc;
    sWC.cbClsExtra = 0;
    sWC.cbWndExtra = 0;
    sWC.hInstance = (HINSTANCE)GetModuleHandle(NULL);
    sWC.hIcon = 0;
    sWC.hCursor = 0;
    sWC.lpszMenuName = 0;
    sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    sWC.lpszClassName = WINDOW_CLASS;

    ATOM wndClass = RegisterClass(&sWC);
    if (!wndClass) {
        throw EXT_EXCEPTION(ENGINE_WINDOW_ERROR_CLASS_REGISTER);
    }
    // Create the eglWindow
    int createFlags = WS_VISIBLE | WS_SYSMENU;
    int showFlags = SWP_SHOWWINDOW;
    if (!m_setup.Visible) {
        createFlags = 0;
        showFlags = 0;
    }

    RECT    sRect;
    SetRect(&sRect, 0, 0, m_setup.Width, m_setup.Height);
    AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0);
    m_window = CreateWindow( WINDOW_CLASS, L"GLESPlayer", createFlags, 0, 0, m_setup.Width, m_setup.Height, NULL, NULL, (HINSTANCE)sWC.hInstance, NULL);
    if (!m_window) {
        throw EXT_EXCEPTION(ENGINE_WINDOW_ERROR_CREATE);
    }
    // Ensure that the window client area matches the given dimensions
    WINDOWINFO winInfo;
    GetWindowInfo(m_window, &winInfo);
    DWORD dx = (winInfo.rcWindow.right - winInfo.rcWindow.left) - (winInfo.rcClient.right - winInfo.rcClient.left);
    DWORD dy = (winInfo.rcWindow.bottom - winInfo.rcWindow.top) - (winInfo.rcClient.bottom - winInfo.rcClient.top);
    SetWindowPos(m_window, 0, winInfo.rcWindow.left, winInfo.rcWindow.top, m_setup.Width + dx, m_setup.Height+ dy, showFlags);
}

WinWindow::~WinWindow() {
    CloseWindow(m_window);
    UnregisterClass(WINDOW_CLASS, (HINSTANCE)GetModuleHandle(NULL));
}

WND_STATUS WinWindow::ProcessMessage() const {
    if (!m_window)
        return WND_FAIL;
    MSG msg;
    if (PeekMessage(&msg, NULL, 0, 0, true)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    if (msg.message == WM_QUIT || msg.message == WM_DESTROY) {
        return WND_QUIT;
    }
    return WND_OK;
}

LRESULT __stdcall WinWindow::wndProc(HWND in_hWnd, UINT in_message, WPARAM in_wParam, LPARAM in_lParam) {
    switch (in_message) {
        case WM_CLOSE:
            PostQuitMessage(0);
            return 1;
    }
    return DefWindowProc(in_hWnd, in_message, in_wParam, in_lParam);
}

inline uint32_t WinWindow::GetWidth() const {
    return m_setup.Width;
}

inline uint32_t WinWindow::GetHeight() const {
    return m_setup.Height;
}

inline WND_HANDLE WinWindow::GetWindowHandle() const {
    return m_window;
}

IEngineWindowPtr CreateEngineWindow(const WindowSetup& setup)
{
    return make_shared<WinWindow>(setup);
}